#include"tss.h"
#include"print.h"
#include"global.h"
#include"memory.h"
#include"string.h"

typedef struct{
	uint32_t backlink;
	uint32_t* esp0;
	uint32_t ss0;	
	uint32_t* esp1;
	uint32_t ss1;
	uint32_t* esp2;
	uint32_t ss2;
	uint32_t (*eip)(void);
	uint32_t eflags;
	uint32_t eax;
	uint32_t ecx;
	uint32_t edx;
	uint32_t ebx;
	uint32_t esp;
	uint32_t ebp;
	uint32_t esi;
	uint32_t edi;
	uint32_t es;
	uint32_t cs;
	uint32_t ss;
	uint32_t ds;
	uint32_t fs;
	uint32_t gs;
	uint32_t ldt;
	uint32_t trace;
	uint32_t io_base;
}tss;

//let complier not add extra space make sure gdt_des size is 8
#pragma pack(1)
typedef struct{
	uint16_t limit_low;
	uint16_t base_low;
	uint8_t base_mid;
	uint16_t attr;
	uint8_t base_hi;
}gdt_des;
#pragma pack()

static tss task_state;

void update_tss_esp0(task_tcb* thread){
	if(thread->page_dir!=NULL){
		task_state.esp0=(uint32_t*)((uint32_t)thread+PG_SIZE);
	}
}

static void make_gdt_des(gdt_des* des,uint32_t base,uint32_t limit,uint16_t attr){
	des->limit_low=(uint16_t)(limit & 0xffff);	
	des->base_low=(uint16_t)(base & 0xffff);
	des->base_mid= (uint8_t)(base>>16 & 0xff);
	des->base_hi=(uint8_t)(base>>24 & 0xff);
	des->attr= (limit >>8 & 0xf00) | (attr & 0xf0ff);	
}

void init_tss(void){
	gdt_des* gdt=(gdt_des*)GDT_TABLE;
	memset((void*)&task_state,0,sizeof(tss));
	task_state.ss0=K_STACK_SEL;
	task_state.io_base=sizeof(tss);
	make_gdt_des(gdt+4,0,0xfffff,G_4K|U_CODE_X);
	make_gdt_des(gdt+5,0,0xfffff,G_4K|U_DATA_RW);
	make_gdt_des(gdt+6,(uint32_t)&task_state,sizeof(task_state)-1,TSS_ATTR);
	uint64_t oprand=((GDT_LEN-1) | ((uint64_t)gdt <<16));
	asm volatile("lgdt %0"::"m"(oprand));
	asm volatile("ltr %w0"::"r"(TSS_SEL));
	put_str("tss init done\n");
}
